//
// Created by shengym on 2019-07-07.
//

#include "lib/common.h"
#include "message_objecte.h"

static int count;

static void sig_int(int signo) 
{
    printf("\nreceived %d datagrams\n", count);
    exit(0);
}


int main(int argc, char **argv) 
{
    if (argc != 2) 
    {
        error(1, 0, "usage: tcpsever <sleepingtime>");
    }

    //建立监听
    int sleepingTime = atoi(argv[1]);

    //创建一个本地 TCP 监听套接字
    int listenfd;
    listenfd = socket(AF_INET, SOCK_STREAM, 0);

    //绑定该套接字到本地端口和 ANY 地址上
    struct sockaddr_in server_addr;
    bzero(&server_addr, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    server_addr.sin_port = htons(SERV_PORT);

    int rt1 = bind(listenfd, (struct sockaddr *) &server_addr, sizeof(server_addr));
    if (rt1 < 0) {
        error(1, errno, "bind failed ");
    }

    //调用 listen 和 accept 完成被动套接字转换和监听
    int rt2 = listen(listenfd, LISTENQ);
    if (rt2 < 0) {
        error(1, errno, "listen failed ");
    }

    signal(SIGINT, sig_int);
    signal(SIGPIPE, SIG_IGN);

    int connfd;
    struct sockaddr_in client_addr;
    socklen_t client_len = sizeof(client_addr);

    if ((connfd = accept(listenfd, (struct sockaddr *) &client_addr, &client_len)) < 0) {
        error(1, errno, "bind failed ");
    }

    messageObject message;
    count = 0;

    //从建立的连接套接字上读取数据，解析报文，根据消息类型进行不同的处理
    for (;;) 
    {
        int n = read(connfd, (char *) &message, sizeof(messageObject));
        if (n < 0) 
        {
            error(1, errno, "error read");
        } 
        else if (n == 0) 
        {
            error(1, 0, "client closed \n");
        }

        printf("received %d bytes\n", n, message);
        count++;

        switch (ntohl(message.type)) 
        {
            case MSG_TYPE1 : //处理 MSG_TYPE1 的消息
                printf("process  MSG_TYPE1 \n");
                break;

            case MSG_TYPE2 : //处理 MSG_TYPE2 的消息
                printf("process  MSG_TYPE2 \n");
                break;

            //处理 MSG_PING 类型的消息。
            //通过休眠来模拟响应是否及时，然后调用 send 函数发送一个 PONG 报文，向客户端表示“还活着”的意思
            case MSG_PING: 
            {
                messageObject pong_message;
                pong_message.type = MSG_PONG;
                sleep(sleepingTime);
                ssize_t rc = send(connfd, (char *) &pong_message, sizeof(pong_message), 0);
                if (rc < 0)
                    error(1, errno, "send failure");
                break;
            }

            default : //异常处理，因为消息格式不认识，所以程序出错退出
                error(1, 0, "unknown message type (%d)\n", ntohl(message.type));
        }

    }

}


